package org.example.studentsystem_back.util;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;

import java.security.spec.PKCS8EncodedKeySpec;
import java.util.*;
import javax.crypto.Cipher;
import javax.security.auth.DestroyFailedException;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.KeyFactory;

/**
 * 备注，解密前台公钥加密的数据，请调用decryptWithPrivate方法
 * 每次重启之后，都会生成一个一对新的公私钥
 */
public class RSAUtil {

    //秘钥大小
    private static final int KEY_SIZE = 1024;

    //后续放到常量类中去
    public static String PRIVATE_KEY = "privateKey";
    public static String PUBLIC_KEY = "publicKey";

    private static KeyPair keyPair;

    private static Map<String, String> rsaMap;

    private static org.bouncycastle.jce.provider.BouncyCastleProvider bouncyCastleProvider = null;

    //BouncyCastleProvider内的方法都为静态方法，GC不会回收
    public static synchronized org.bouncycastle.jce.provider.BouncyCastleProvider getInstance() {
        if (bouncyCastleProvider == null) {
            bouncyCastleProvider = new org.bouncycastle.jce.provider.BouncyCastleProvider();
        }
        return bouncyCastleProvider;
    }

    //生成RSA，并存放
    static {
        try {
            //通过以下方法，将每次New一个BouncyCastleProvider，可能导致的内存泄漏
   /*         Provider provider =new org.bouncycastle.jce.provider.BouncyCastleProvider();
            Security.addProvider(provider);
            KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", provider);*/
            //解决方案
            KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");

            SecureRandom random = new SecureRandom();
            generator.initialize(KEY_SIZE,random);
            keyPair = generator.generateKeyPair();
            PublicKey publicKey = keyPair.getPublic();
            String publicKeyStr = Base64.getEncoder().encodeToString(publicKey.getEncoded());
            PrivateKey privateKey = keyPair.getPrivate();
            String privateKeyStr = Base64.getEncoder().encodeToString(privateKey.getEncoded());
            PRIVATE_KEY=privateKeyStr;
            PUBLIC_KEY=publicKeyStr;
            //将公钥和私钥存放，登录时会不断请求获取公钥
            //建议放到redis的缓存中，避免在分布式场景中，出现拿着server1的公钥去server2解密的问题
            //storeRSA();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }

    public static void pen() throws NoSuchAlgorithmException {
        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");

        SecureRandom random = new SecureRandom();
        generator.initialize(KEY_SIZE,random);
        keyPair = generator.generateKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        String publicKeyStr = Base64.getEncoder().encodeToString(publicKey.getEncoded());
        PrivateKey privateKey = keyPair.getPrivate();
        String privateKeyStr = Base64.getEncoder().encodeToString(privateKey.getEncoded());
        PRIVATE_KEY=privateKeyStr;
        PUBLIC_KEY=publicKeyStr;
    }

    /**
     * 将RSA存入缓存
     */
    private static void storeRSA() {
        rsaMap = new HashMap<>();
        PublicKey publicKey = keyPair.getPublic();
        String publicKeyStr = Base64.getEncoder().encodeToString(publicKey.getEncoded());
        rsaMap.put(PUBLIC_KEY, publicKeyStr);

        PrivateKey privateKey = keyPair.getPrivate();
        String privateKeyStr = Base64.getEncoder().encodeToString(privateKey.getEncoded());
        rsaMap.put(PRIVATE_KEY, privateKeyStr);
    }

    /**
     * 私钥解密(解密前台公钥加密的密文)
     *
     * @param encryptText 公钥加密的数据
     * @return 私钥解密出来的数据
     * @throws Exception e
     */
    public static String decryptWithPrivate(String encryptText) throws Exception {
        if (StringUtils.isBlank(encryptText)) {
            return null;
        }
        byte[] en_byte = Base64.getDecoder().decode(encryptText.getBytes());
        //可能导致内存泄漏问题
     /*   Provider provider = new org.bouncycastle.jce.provider.BouncyCastleProvider();
        Security.addProvider(provider);
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", provider);*/
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        PrivateKey privateKey = keyPair.getPrivate();
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] res = cipher.doFinal(en_byte);
        return new String(res);
    }

    /**
     * java端 使用公钥加密(此方法暂时用不到)
     *
     * @param plaintext 明文内容
     * @return byte[]
     * @throws UnsupportedEncodingException e
     */
    public static byte[] encrypt(String plaintext,String key) throws Exception {
        String encode = URLEncoder.encode(plaintext, "utf-8");
        RSAPublicKey rsaPublicKey= (RSAPublicKey) getPublicKeyFromString(key);
        //获取公钥指数
        BigInteger e = rsaPublicKey.getPublicExponent();
        //获取公钥系数
        BigInteger n = rsaPublicKey.getModulus();
        //获取明文字节数组
        BigInteger m = new BigInteger(encode.getBytes());
        //进行明文加密
        BigInteger res = m.modPow(e, n);
        //保存公钥和私钥
        RSAFile.setInformation(getPublicKey(),getPrivateKey());
        return res.toByteArray();

    }



    /**
     * java端 使用私钥解密(此方法暂时用不到)
     *
     * @param cipherText 加密后的字节数组
     * @return 解密后的数据
     * @throws UnsupportedEncodingException e
     */
    public static String decrypt(byte[] cipherText,String key0) throws Exception {
        String key=RSAFile.getInformation(key0);
        RSAPrivateKey prk= (RSAPrivateKey) getPrivateKeyFromString(key);
        // 获取私钥参数-指数/系数
        BigInteger d = prk.getPrivateExponent();
        BigInteger n = prk.getModulus();
        // 读取密文
        BigInteger c = new BigInteger(cipherText);
        // 进行解密
        BigInteger m = c.modPow(d, n);
        // 解密结果-字节数组
        byte[] mt = m.toByteArray();
        //转成String,此时是乱码
        String en = new String(mt);
        //再进行编码,最后返回解密后得到的明文
        return URLDecoder.decode(en, "UTF-8");
    }


    /**
     * 获取公钥
     *
     * @return 公钥
     */
    public static String getPublicKey() {
        return PUBLIC_KEY;
    }

    /**
     * 获取私钥
     *
     * @return 私钥
     */
    public static String getPrivateKey() {
        return PRIVATE_KEY;
    }

    /**
     * String转公钥PublicKey
     * @param key
     * @return
     * @throws Exception
     */
    public static PrivateKey getPrivateKeyFromString(String key) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(key);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(keySpec);
    }

    /**
     * String转私钥PrivateKey
     * @param key
     * @return
     * @throws Exception
     */
    public static PublicKey getPublicKeyFromString(String key) throws Exception {
        // 去除可能的头部和尾部
        String cleanedKey = key.replace("-----BEGIN PUBLIC KEY-----", "")
                .replace("-----END PUBLIC KEY-----", "")
                .replaceAll("\\s", ""); // 去除所有空白字符

        byte[] keyBytes = Base64.getDecoder().decode(cleanedKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(keySpec);
    }


    public static void main(String[] args) throws Exception {
        System.out.println(RSAUtil.getPrivateKey());
        System.out.println(RSAUtil.getPublicKey());
        byte[] usernames = RSAUtil.encrypt("username",getPublicKey());
        System.out.println(Arrays.toString(usernames));
        System.out.println(RSAUtil.decrypt(usernames,getPrivateKey()));
    }
}